package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.consts.SrbMqConst;
import com.atguigu.srb.base.dto.SmsDTO;
import com.atguigu.srb.base.result.ResponseEnum;
import com.atguigu.srb.base.util.JwtUtils;
import com.atguigu.srb.base.util.SrbAssert;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.TransFlowService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.util.LendNoUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2023-03-25
 */
@Service
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
    @Resource
    UserInfoService userInfoService;
    @Resource
    TransFlowService transFlowService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    RabbitTemplate rabbitTemplate;
    //充值：构建自动提交的表单
    @Override
    public String charge(BigDecimal amount, String token) {
        //1、验证登录状态+验证用户账户状态(锁定 绑定hfb..)
        UserInfo userInfo = userInfoService.checkUserStatus(token);
        //2、校验充值金额
        SrbAssert.assertTrue(amount.doubleValue()>0, ResponseEnum.NOT_CHARGE_ERROR);
        //3、准备表单参数
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);//商户id
        String chargeNo = LendNoUtils.getChargeNo();
        params.put("agentBillNo", chargeNo);//商户充值单号：必须唯一  对账
        params.put("bindCode",userInfo.getBindCode());//会员在hfb开通的账户的唯一编号
        params.put("chargeAmt",amount);//充值金额  用户充值提现不扣除手续费：借款人借款成功扣除一部分、投资人回款时扣除一部分手续费
        params.put("feeAmt","0");//手续费
        //http://192.168.1.171/api/core/userAccount/notify
        params.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);//hfb处理用户充值成功后的回调接口，地址必须改为 nginx服务器的地址
        params.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);//hfb处理用户充值成功同步给用户响应的地址
        params.put("timestamp",System.currentTimeMillis());//时间戳
        params.put("sign", RequestHelper.getSign(params));//以上数据生成的签名  加密字符串

        //保存交易日志
        TransFlow transflow = new TransFlow();
        transflow.setUserId(userInfo.getId());
        transflow.setUserName(userInfo.getName());
        transflow.setTransNo(chargeNo);
        transflow.setTransType(1);//1：充值
        transflow.setTransTypeName("充值");
        transflow.setTransAmount(amount);
        transFlowService.save(transflow);

        //参数1：汇付宝充值接口地址   参数2：该接口需要的参数
        return FormHelper.buildForm(HfbConst.RECHARGE_URL,params);
    }
    //保证幂等性：保证携带了相同唯一token的请求 业务方法只处理一次即可
    // 数据库：  第一步生成充值表单时将充值日志存到数据库日志表中(transflow表) 状态设置为0(交易未完成)
    //           第二步回调时 如果transflow表中存在对应的日志且状态为0表示第一次回调，执行业务代码并更新日志状态为1
    @Override
    public String rechargeNotify(Map<String, Object> map) {
        //1、验签
        boolean signEquals = RequestHelper.isSignEquals(map);
        //以前自己的业务抛出异常 由全局异常处理器处理返回一个失败的ResponseVo给前端
        //当前接口由hfb来调用，抛出异常由全局异常处理器返回一个失败的ResponseVo给hfb
//        SrbAssert.assertTrue(signEquals,ResponseEnum.WX_LOGIN_CALLBACK_TOKEN_ERROR);
        if(!signEquals){
            log.error("充值回调失败,签名错误：{}", JSON.toJSONString(map));
            return "fail";
        }
        //2、验证hfb处理业务的状态
        String resultCode = map.get("resultCode").toString();//0001代表成功
        String resultMsg = map.get("resultMsg").toString();//状态码描述
        String agentBillNo = map.get("agentBillNo").toString();//上一步生成的商户的订单号
        String bindCode = map.get("bindCode").toString();//充值的会员的hfb账号
        String chargeAmt = map.get("chargeAmt").toString();//充值金额
        String hyFee = map.get("hyFee").toString();//hfb收取商家的手续费
        String mchFee = map.get("mchFee").toString();//商户扣除的用户的手续费
//        SrbAssert.assertTrue("0001".equals(resultCode),ResponseEnum.WEIXIN_CALLBACK_PARAM_ERROR);
        if(!"0001".equals(resultCode)){
            log.error("充值回调失败,状态码错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        //3、验证充值金额+ 幂等性验证
        //3.1 幂等性验证
        //根据agentBillNo查询交易日志
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        if(transFlow==null){
            //交易号不存在
            log.error("充值回调失败,交易编号错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        if(transFlow.getStatus()==1){
            //交易号存在 并且已完成
            return "success";
        }
        //交易号存在 并且交易未完成
        //3.2 校验充值金额
        if(transFlow.getTransAmount().compareTo(new BigDecimal(chargeAmt))!=0){
            log.error("充值回调失败,hfb充值金额错误: {}", JSON.toJSONString(map));
            return "fail";
        }

        //4、更新用户账户余额
        //bindCode在userInfo表中对应唯一的一个用户
        //userAccount表没有存bindCode，绑定的是userId
        // 根据bindCode查询userId，根据userId更新userAccount账户金额在之前基础上+本次充值金额
        userAccountService.updateAmountByBindCode(new BigDecimal(chargeAmt),bindCode);
        //5、更新交易日志
        transFlow.setStatus(1);
        transFlow.setCreateTime(null);
        transFlow.setUpdateTime(null); //修改update的值为null  触发自动填充
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.updateById(transFlow);

        //发送 充值成功的消息： 消息内容
        UserInfo userInfo = userInfoService.getById(transFlow.getUserId());
        SmsDTO smsDTO = new SmsDTO();
        smsDTO.setMobile(userInfo.getMobile());
        smsDTO.setType(3);
        smsDTO.setParams(Arrays.asList("cz"));
        rabbitTemplate.convertAndSend(SrbMqConst.SMS_EXCHANGE,
                //发送短信时 需要使用不同的短信模板
                //type=3表示充值成功
                SrbMqConst.SMS_ROUTING_KEY, smsDTO);
        //返回成功
        return "success";
    }

    @Override
    public boolean updateAmountByBindCode(BigDecimal amount, String bindCode) {
        return baseMapper.updateAmountByBindCode(amount,bindCode);
    }

    @Override
    public UserAccount getUserAccountByUserId(Long userId) {
        return  this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId ,userId));
    }

    @Override
    public boolean updateInvestAmountByUserId(Long userId, String voteAmt) {

        return baseMapper.updateInvestAmountByUserId(userId,voteAmt);
    }

    @Override
    public boolean updateFreezeAmountByUserId(Long investUserId, BigDecimal investAmount) {
        return baseMapper.updateFreezeAmountByUserId(investUserId,investAmount);
    }

    @Override
    public String withDraw(String token, BigDecimal amount) {
        //检查用户的状态
        UserInfo userInfo = userInfoService.checkUserStatus(token);
        //提现金额必须大于0
        SrbAssert.assertTrue(amount.compareTo(new BigDecimal(0))>0 ,ResponseEnum.ERROR);
        //提现金额不能超过自己的账户余额
        UserAccount userAccount = this.getUserAccountByUserId(userInfo.getId());
        SrbAssert.assertTrue(userAccount.getAmount().compareTo(amount)>=0 ,ResponseEnum.WITH_DRAW_ACCOUNT_ERROR);
        //创建自动提交的表单字符串
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        String withdrawNo = LendNoUtils.getWithdrawNo();
        params.put("agentBillNo", withdrawNo);//本次提现操作的单号
        params.put("bindCode",userInfo.getBindCode());//提现用户的hfb账户的bindcode
        params.put("fetchAmt",amount);
        params.put("feeAmt","0");
        params.put("returnUrl",HfbConst.WITHDRAW_RETURN_URL);
        params.put("notifyUrl",HfbConst.WITHDRAW_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign",RequestHelper.getSign(params));

        //日志：
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());//提款人id
        transFlow.setUserName(userInfo.getName());//
        transFlow.setTransNo(withdrawNo);//数据库表唯一键约束 此值已经在上面保存了一个日志
        transFlow.setTransType(2);//2：提现
        transFlow.setTransTypeName("提现");
        transFlow.setTransAmount(amount);
        transFlow.setStatus(0);
        transFlowService.save(transFlow);

        return FormHelper.buildForm(HfbConst.WITHDRAW_URL , params);
    }

    @Override
    public String notifyWithdraw(Map<String, Object> map) {
        //1、校验hfb返回数据的安全(签名、状态码)
        boolean flag = RequestHelper.isSignEquals(map);
        if(!flag){
            log.error("提现回调失败，签名错误：{}",JSON.toJSONString(map));
            return "fail";
        }
        if(!"0001".equals(map.get("resultCode"))){
            log.error("提现回调失败，汇付宝业务处理错误：{}",JSON.toJSONString(map));
            return "fail";
        }
        //2、校验提现金额、幂等性
        String agentBillNo = map.get("agentBillNo").toString();//提现单号
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        if(transFlow.getStatus()==1){
            return "success";
        }
        BigDecimal bigDecimal = new BigDecimal(map.get("fetchAmt").toString());
        if(transFlow.getTransAmount().compareTo(bigDecimal)!=0){
            log.error("提现回调失败，提现金额错误：{}",JSON.toJSONString(map));
            return "fail";
        }
        //3、更新用户账户余额、日志
        //更新日志状态
        transFlow.setMemo(JSON.toJSONString(map));
        transFlow.setStatus(1);
        transFlow.setUpdateTime(null);
        transFlowService.updateById(transFlow);

        Long userId = transFlow.getUserId();
        //扣除账户提现的金额
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));

        userAccount.setAmount(userAccount.getAmount().subtract(bigDecimal));
        userAccountService.updateById(userAccount);
        //发送短信通知提现成功： 异步解耦合(rabbitmq)

        UserInfo userInfo = userInfoService.getById(transFlow.getUserId());
        SmsDTO smsDTO = new SmsDTO();
        smsDTO.setMobile(userInfo.getMobile());
        smsDTO.setType(4);
        smsDTO.setParams(Arrays.asList("tx"+transFlow.getTransAmount()));
        rabbitTemplate.convertAndSend(SrbMqConst.SMS_EXCHANGE,
                //发送短信时 需要使用不同的短信模板
                //type=3表示充值成功
                SrbMqConst.SMS_ROUTING_KEY, smsDTO);
        return "success";
    }
}
